home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
301-325
/
disk_319
/
cnewssrc
/
cnews.src.lzh
/
relay
/
relaynews.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-07-31
|
13KB
|
492 lines
/*
* relaynews - relay Usenet news (version C)
* See the file COPYRIGHT for the copyright notice.
*
* relaynews should be setuid-news, setgid-news. You'll need to install
* setnewsids setuid-root if setuid(geteuid()) doesn't work on your
* machine (e.g. on V7 and possibly SystemIII).
*
* Written by Geoff Collyer, 15-20 November 1985 and revised periodically
* since.
*
* relaynews parses article headers, rejects articles by newsgroup &
* message-id, files articles, updates the active & history files,
* transmits articles, and honours (infrequent) control messages, which do
* all sorts of varied and rococo things. Control messages are implemented
* by separate programs. relaynews reads a "sys" file to control the
* transmission of articles but can function as a promiscuous leaf node
* without one. See ARPA Internet RFC 1036 nee 850 for the whole story.
*
* A truly radical notion: people may over-ride via environment variables
* the compiled-in default directories so IHCC kludges are not needed and
* testing is possible (and encouraged) in alternate directories. This
* does cause a loss of privilege, to avoid spoofing.
*
* The disused old ihave/sendme protocol is dead; it's been broken in
* B news for ages but no one has noticed because it's essentially
* useless on the uucp network, especially when batching news articles.
* It is also only semi-documented, very wasteful and kludgey in the extreme.
* It is unreasonable to expect an ill-documented protocol to be implemented
* in new software, particularly one as kludgey as this one.
* The new ihave sys flag ("I") for NNTP is implemented (trivially).
*
* Portability vs SystemV. relaynews uses dbm(3) and makes no apologies
* for so doing. Imitation UNIX (registered trademark of AT&T in the
* United States) brand operating systems that lack dbm are going to
* have to use my incredibly slow dbm simulation, or another.
*/
#ifdef DEBUG
# define STAT(x) { fprintf(stderr,"%s: %d, ",__FUNC__,__LINE__);\
fprintf x;\
fflush(stderr); }
#else
# define STAT(x)
#endif
#include <stdio.h>
#include <ctype.h>
#include <signal.h> /* to make locking safe */
#ifndef AMIGA
# include <sys/types.h>
#endif /* AMIGA */
#include "libc.h"
#include "news.h"
#include "config.h"
#include "fgetmfs.h"
#include "active.h"
#include "caches.h"
#include "cpu.h"
#include "fileart.h"
#include "headers.h"
#include "history.h"
#include "transmit.h"
/*
* setuid-root program to set ids to news/news & rexec rnews with
* NEWSPERMS in the environment to break loops.
*/
#ifndef SETNEWSIDS
#define SETNEWSIDS "setnewsids"
#endif
/* exports */
char *progname;
boolean okrefusal = YES; /* okay to refuse articles? */
char *exclude = NULL; /* site to exclude, for erik */
boolean histreject = NO; /* keep history of rejects? */
/* internal */
static boolean userealids = NO;
/* imports */
extern int optind; /* set by getopt */
extern char *optarg;
extern statust cpinsart(); /* from procart.c */
/* forwards */
extern void prelude(), setids(), procopts(), redirectlogs(), logfile();
extern void getwdandcd();
extern statust procargs(), relnmprocess(), process(), unbatch();
extern boolean batchln();
FORWARD boolean debugon();
/*
* main - take setuid precautions, switch to "news" ids, ignore signals,
* handle options, lock news system, process files & unlock news system.
*/
int
main(argc, argv)
int argc;
char *argv[];
{
statust status = ST_OKAY;
int redirlogs = 0; /* redirect n std output streams to logs */
char *origdir = NULL; /* current directory at start */
progname = argv[0];
#ifdef CSRIMALLOC
mal_debug(0); /* was 2; 3 is too slow */
mal_leaktrace(0); /* was 1 */
#endif
prelude(argv); /* various precautions; switch to "news" */
/*
* ignore signals (for locking). relaynews runs quickly,
* so don't worry.
*/
#ifdef SIGINT
(void) signal(SIGINT, (sigarg_t)SIG_IGN);
#endif
#ifdef SIGQUIT
(void) signal(SIGQUIT, (sigarg_t)SIG_IGN);
#endif
#ifdef SIGHUP
(void) signal(SIGHUP, (sigarg_t)SIG_IGN);
#endif
#ifdef SIGTERM
(void) signal(SIGTERM, (sigarg_t)SIG_IGN);
#endif
procopts(argc, argv, &redirlogs, &okrefusal);
newslock(); /* done here due to dbm internal cacheing */
if (redirlogs > 0) {
redirectlogs(redirlogs); /* redirect std output streams to logs */
#ifdef MANYERRORS
(void) putc('\n', stderr); /* leave a blank line */
/* prints "Jun 5 12:34:56" */
timestamp(stderr, (time_t *)NULL);
(void) putc('\n', stderr);
#endif
}
getwdandcd(argc, argv, &origdir);
status |= procargs(argc, argv, &origdir);
status |= synccaches(); /* being cautious: write & close caches */
(void) fflush(stdout); /* log file */
(void) fflush(stderr); /* errlog file */
#ifdef notdef
# ifdef CSRIMALLOC
mal_dumpleaktrace(fileno(stderr));
# endif
#endif
newsunlock();
exit(status);
/* NOTREACHED */
}
/*
* reset various environmental things for safety: umask, alarm,
* environment variables (PATH, IFS), standard file descriptors,
* user & group ids.
*/
void
prelude(argv) /* setuid daemon prelude */
char **argv;
{
register char *newpath;
(void) umask(2); /* undo silly umasks, ignore newsumask() */
(void) alarm(0); /* cancel any pending alarm */
newpath = malloc(STRLEN("PATH=") + strlen(newspath()) + 1);
if (newpath == NULL)
exit(1); /* no chatter until stdfdopen */
(void) strcpy(newpath, "PATH=");
(void) strcat(newpath, newspath());
#ifndef AMIGA
if (putenv(newpath) || putenv("IFS= \t\n"))
exit(1); /* no chatter until stdfdopen */
#endif
closeall(1); /* closes all but std descriptors */
stdfdopen(); /* ensure standard descriptors are open */
setids(argv); /* change of real and effective ids */
}
/*
* change real and effective ids to real ids if unprivileged() is called,
* else to effective ("news") ids. ctlfile((char *)0) will trigger a call
* to unprivileged() if any environment variables override the default
* path names. unprivileged() in turn sets userealids.
*
* If setuid(geteuid()) fails, try execing a small, setuid-root program
* to just do "getpwnam(), getgrnam() (with NEWSPERMS set), setgid(),
* setuid()," and exec this program again. If NEWSPERMS is set,
* the failure is a fatal error (recursive loop).
* This program (relaynews) can be setuid-news.
*/
void setids(argv)
char **argv;
{
int newsuid, newsgid;
(void) ctlfile((char *)NULL);
if (userealids)
newsuid = getuid(), newsgid = getgid();
else
newsuid = geteuid(), newsgid = getegid();
if (setgid(newsgid) < 0 || setuid(newsuid) < 0) {
if (getenv("NEWSPERMS") != 0)
error("recursive loop setting ids", "");
execv(ctlfile(SETNEWSIDS), argv);
error("can't exec `%s' to set ids", ctlfile(SETNEWSIDS));
/* NOTREACHED */
}
/* we are now running as news, so you can all relax */
}
/*
* parse options and set flags
*/
void procopts(argc, argv, redirlogsp, okrefusalp)
int argc;
char **argv;
int *redirlogsp;
boolean *okrefusalp;
{
int c, errflg = 0;
while ((c = getopt(argc, argv, "d:inrsx:")) != EOF)
switch (c) {
case 'd': /* -d debug-options; thanks, henry */
if (!debugon(optarg))
errflg++; /* debugon has complained */
break;
case 'i': /* redirect stdout to log (inews) */
*redirlogsp = 1; /* just stdout */
break;
case 'n': /* nntp mode: keep history of rejects */
histreject = YES;
break;
case 'r': /* redirect std. ostreams to logs (rnews) */
*redirlogsp = 2; /* stdout & stderr */
break;
case 's': /* dropping input is serious (inews) */
*okrefusalp = NO;
break;
case 'x': /* -x site: don't send to site */
/* you're welcome, erik */
/* erik says he only needs one -x per inews */
if (exclude != NULL) {
(void) fprintf(stderr,
"%s: more than one -x site (%s)\n", progname, optarg);
errflg++;
} else
exclude = optarg;
break;
default:
errflg++;
break;
}
if (errflg) {
(void) fprintf(stderr,
"usage: %s [-inrs][-d fhlmt][-x site]\n", progname);
(void) fprintf(stderr,
" -df ... file, -dh ... hdr, -dl ... lock,\n");
(void) fprintf(stderr,
" -dm ... match, -dt ... trans\n");
exit(2);
}
}
void unprivileged() /* called if NEWSARTS, NEWSCTL or NEWSBIN present */
{
userealids = YES;
}
STATIC boolean
debugon(dbopt)
register char *dbopt;
{
statust status = YES;
for (; *dbopt != '\0'; dbopt++)
switch (*dbopt) {
case 'f':
filedebug(YES);
break;
case 'h':
hdrdebug(YES);
break;
case 'l':
lockdebug(YES);
break;
case 'm':
matchdebug(YES);
break;
case 't':
transdebug(YES);
break;
default:
status = NO; /* unknown debugging option */
(void) fprintf(stderr, "%s: bad -d %c\n", progname, *dbopt);
break;
}
return status;
}
/*
* Redirect stdout or stderr into log files at known locations.
*/
void redirectlogs(count)
int count;
{
if (count > 0)
logfile(stdout, ctlfile("log"));
if (count > 1)
logfile(stderr, ctlfile("errlog"));
}
void logfile(stream, name) /* redirect stream into name */
FILE *stream;
char *name;
{
if (freopen(name, "a", stream) == NULL)
errunlock("can't redirect standard stream to `%s'", name);
}
/*
* if argv contains relative file name arguments, save current directory name
* in malloced memory, through origdirp.
* then change directory to the spool directory ($NEWSARTS).
*/
void getwdandcd(argc, argv, origdirp)
int argc;
char **argv;
char **origdirp;
{
register int argind;
boolean needpwd = NO;
char dirtmp[MAXPATH]; /* much bigger than needed */
for (argind = optind; argind < argc; argind++)
if (argv[argind][0] != FNDELIM)
needpwd = YES;
*origdirp = "/???"; /* pessimism */
if (needpwd && getcwd(dirtmp, sizeof dirtmp) != 0)
*origdirp = dirtmp;
*origdirp = strsave(*origdirp); /* save a smaller copy */
cd(fullartfile((char *)NULL)); /* move to spool directory */
}
/*
* process files named as arguments (or implied)
*/
statust procargs(argc, argv, origdirp)
int argc;
char **argv;
char **origdirp;
{
register statust status = ST_OKAY;
if (optind == argc)
status |= process(stdin, "stdin");
else
for (; optind < argc; optind++)
status |= relnmprocess(argv[optind], *origdirp);
nnfree(origdirp);
return status;
}
statust relnmprocess(name, origdir) /* process a (relative) file name */
char *name, *origdir;
{
register statust status = ST_OKAY;
register FILE *in;
register char *fullname;
fullname = nemalloc((unsigned)strlen(origdir) + STRLEN(SFNDELIM) +
strlen(name) + 1);
fullname[0] = '\0';
if (name[0] != FNDELIM) { /* relative path */
(void) strcat(fullname, origdir);
(void) strcat(fullname, SFNDELIM);
}
(void) strcat(fullname, name);
in = fopenwclex(fullname, "r");
if (in != NULL) {
status |= process(in, fullname);
(void) nfclose(in);
}
free(fullname);
return status;
}
/*
* process - process input file
* If it starts with '#', assume it's a batch and unravel it,
* else it's a single article, so just inject it.
*/
statust process(in, inname)
FILE *in;
char *inname;
{
register int c;
if ((c = getc(in)) == EOF)
return ST_OKAY; /* normal EOF */
(void) ungetc(c, in);
if (c == '#')
return unbatch(in, inname);
else
return cpinsart(in, inname, MAXLONG, NO);
}
/*
* Unwind "in" and insert each article.
* For each article, call cpinsart to copy the article from "in" into
* a (temporary) file in the news spool directory and rename the temp file
* to the correct final name if it isn't right already.
*
* If the unbatcher gets out of sync with the input batch, the unbatcher
* will print and discard each input line until it gets back in sync.
*/
statust unbatch(in, inname)
register FILE *in;
char *inname;
{
register int c;
/* register */ char *line;
register statust status = ST_OKAY;
long charcnt;
while (!(status&ST_DISKFULL) && (c = getc(in)) != EOF) {
(void) ungetc(c, in);
STAT((stderr, "read char '%c'\n", c));
while ((line = fgetms(in)) != NULL &&
!batchln(line, &charcnt)) { /* returns charcnt */
status |= ST_DROPPED;
(void) fprintf(stderr,
"\n%s: unbatcher out of synch: ", progname);
(void) fputs(line, stderr);
free(line);
}
STAT((stderr, "count (%ld) line okay: %s", charcnt, line));
nnfree(&line); /* free "#! rnews n" */
if (!feof(in))
status |= cpinsart(in, inname, charcnt, YES);
}
if (ferror(in))
errunlock("error reading `%s'", inname);
return status;
}
/*
* Is line a batcher-produced line (#! rnews count)?
* If so, return the count through charcntp.
* This is slightly less convenient than sscanf, but a lot smaller.
*/
boolean batchln(line, charcntp)
register char *line;
register long *charcntp;
{
extern long atol();
register char *countp;
static char batchtext[] = "#! rnews ";
countp = line + STRLEN(batchtext);
STAT((stderr, "line: %s\ncount: %s\n", line, countp));
if (STREQN(line, batchtext, STRLEN(batchtext)) &&
isascii(*countp) && isdigit(*countp)) {
*charcntp = atol(countp);
return YES;
} else {
*charcntp = 0;
return NO;
}
}